namespace SimpleWebServices.Data
{
    using System;
    using System.Collections.ObjectModel;
    using System.Xml;
    using SimpleWebServices;
    using SimpleWebServices.Security.Cryptography;
    using SimpleWebServices.Web;
    using Xunit;

    public sealed class EntityFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<Entity>()
                .DerivesFrom<object>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .Result);
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new Entity());
        }

        [Fact]
        public void ctor_string()
        {
            Assert.NotNull(new Entity("example"));
        }

        [Fact]
        public void ctor_stringEmpty()
        {
            Assert.NotNull(new Entity(string.Empty));
        }

        [Fact]
        public void ctor_stringNull()
        {
            Assert.NotNull(new Entity(null as string));
        }

        [Fact]
        public void opImplicit_string_Entity()
        {
            string expected = "test";

            Entity obj = new Entity();
            obj.Append(expected);

            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void opImplicit_string_EntityNull()
        {
            string expected = null;
            string actual = null as Entity;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_bool()
        {
            bool value = true;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_boolFalse()
        {
            bool value = false;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_byte()
        {
            byte value = 1;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_byteMinValue()
        {
            byte value = byte.MinValue;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_char()
        {
            char value = 'a';

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_charMinValue()
        {
            char value = char.MinValue;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_DateTime()
        {
            DateTime value = DateTime.UtcNow;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = value.ToXmlString();
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_DateTimeMinValue()
        {
            DateTime value = DateTime.MinValue;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_DateTimeOffset()
        {
            DateTimeOffset value = DateTimeOffset.Now;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_DateTimeOffsetMinValue()
        {
            DateTimeOffset value = DateTimeOffset.MinValue;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_decimal()
        {
            decimal value = 1.23m;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_decimalZero()
        {
            decimal value = 0m;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_double()
        {
            double value = 1.23D;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_doubleZero()
        {
            double value = 0;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_float()
        {
            float value = 1.23f;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_floatZero()
        {
            float value = 0f;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_Guid()
        {
            Guid value = Guid.NewGuid();

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_GuidDefault()
        {
            Guid value = new Guid();

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_int()
        {
            int value = 123;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_intZero()
        {
            int value = 0;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_long()
        {
            long value = 123;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_longZero()
        {
            long value = 0;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_short()
        {
            short value = 1;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_shortZero()
        {
            short value = 0;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_string()
        {
            string expected = "test";

            Entity obj = new Entity();
            obj.Append(expected);

            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_stringEmpty()
        {
            string expected = "test" + Environment.NewLine;

            Entity obj = new Entity();
            obj.Append("test");
            obj.Append(string.Empty);

            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_stringNull()
        {
            string expected = "test";

            Entity obj = new Entity();
            obj.Append(expected);
            obj.Append(null as string);

            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_TimeSpan()
        {
            TimeSpan value = new TimeSpan(1);

            Entity obj = new Entity();
            obj.Append(value);

            string expected = XmlConvert.ToString(value);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_TimeSpanMinValue()
        {
            TimeSpan value = TimeSpan.MinValue;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_IRecord()
        {
            IRecord value = new GenericResource
            {
                Title = "Example"
            };

            Entity obj = new Entity();
            obj.Append(value);

            string expected = MD5Hash.Compute(value.Entity);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_IRecordNull()
        {
            IRecord value = null;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_object()
        {
            string expected = "http://example.com/";

            object value = new Uri(expected);

            Entity obj = new Entity();
            obj.Append(value);

            expected = MD5Hash.Compute(expected);
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_objectIEnumerable()
        {
            Collection<string> value = new Collection<string>();
            value.Add("example");

            Entity obj = new Entity();
            obj.Append(value);

            string expected = "Gnmk1g3mcY6OWzJuM4rlMw==";
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_objectIEnumerableEmpty()
        {
            Collection<string> value = new Collection<string>();

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Append_objectNull()
        {
            object value = null;

            Entity obj = new Entity();
            obj.Append(value);

            string expected = string.Empty;
            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Indent()
        {
            string expected = "\t\t";

            Entity obj = new Entity();
            obj.Indent();
            obj.Indent();
            obj.Append(string.Empty);

            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Unindent()
        {
            string expected = "\t";

            Entity obj = new Entity();
            obj.Indent();
            obj.Indent();
            obj.Unindent();
            obj.Append(string.Empty);

            string actual = obj;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Unindent_whenNotIndented()
        {
            Assert.Throws<InvalidOperationException>(() => new Entity().Unindent());
        }

        [Fact]
        public void op_ToString()
        {
            string expected = string.Empty;
            string actual = new Entity().ToString();

            Assert.Equal<string>(expected, actual);
        }
    }
}